/**
 * User: alesj
 * Date: 2005.6.6
 * Time: 11:28:49
 * 
 * (C) Genera Lynx d.o.o.
 */

package com.alesj.newsfeed.web.form;

import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.mail.internet.InternetAddress;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.alesj.newsfeed.data.Account;
import com.alesj.newsfeed.data.Contact;
import com.alesj.newsfeed.data.Group;
import com.alesj.newsfeed.data.GroupEntry;
import com.alesj.newsfeed.data.Message;
import com.alesj.newsfeed.data.MessageCriteria;
import com.alesj.newsfeed.file.ByteAdapter;
import com.alesj.newsfeed.web.object.FileUploadBean;
import com.alesj.newsfeed.web.object.SelectSend;
import com.generalynx.common.constants.WebConst;
import com.generalynx.common.utils.CollectionUtils;
import com.generalynx.ecos.data.User;
import com.generalynx.ecos.web.object.BeanCollection;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.validation.BindException;
import org.springframework.web.bind.RequestUtils;
import org.springframework.web.servlet.ModelAndView;

public class SendForm extends ExecutorHolderController {

    private String resultView;

    public SendForm() {
        setCommandClass(SelectSend.class);
    }

    protected Map referenceData(HttpServletRequest request) throws Exception {
        Map<String, Object> model = new HashMap<String, Object>();
        Collection groups = getBasicDAO().findEntity(Group.class);
        model.put("groups", groups);
        return model;
    }

    protected ModelAndView onSubmit(HttpServletRequest request,
                                    HttpServletResponse response,
                                    Object command,
                                    BindException errors) throws Exception {
        SelectSend selectSend = (SelectSend)command;
        HttpSession session = request.getSession(false);
        if (selectSend.isPrepare()) {
            Collection contacts;
            Set<MessageCriteria> criteria = new HashSet<MessageCriteria>();
            if (selectSend.hasQuery()) {
                contacts = getBasicDAO().findContacts(selectSend.getQuery(), criteria);    
            } else {
                boolean isConjunction = "AND".equalsIgnoreCase(selectSend.getType());
                Set excluded = new TreeSet();
                int[] exludedIds = selectSend.getExclude();
                CollectionUtils.asSet(exludedIds, excluded);
                int[] entriesIds = selectSend.getEntry();
                for (int i = 0; entriesIds != null && i < entriesIds.length; i++) {
                    int entryId = entriesIds[i];
                    MessageCriteria mc = new MessageCriteria();
                    Group group;
                    if (entryId > 0) {
                        GroupEntry ge = getBasicDAO().loadGroupEntry(entryId);
                        group = ge.getGroup();
                        mc.setGroupEntry(ge);
                    } else {
                        group = getBasicDAO().loadGroup(-entryId);
                    }
                    mc.setGroup(group);
                    mc.setConjunction(isConjunction);
                    mc.setExcluded(excluded.contains(group.getId()));
                    criteria.add(mc);
                }
                contacts = getBasicDAO().findContacts(entriesIds, exludedIds, isConjunction);
            }
            if (contacts == null || contacts.isEmpty()) {
                return new ModelAndView("msgView", WebConst.MSG_CODE, "empty_contacts");
            } else {
                Collection bc = new BeanCollection(contacts);
                session.setAttribute("contacts", bc);
                session.setAttribute("accounts", getBasicDAO().findEntity(Account.class));
                session.setAttribute(WebConst.CRITERIA, criteria);
                return new ModelAndView(getSuccessView());
            }
        } else {
            User creator = (User)session.getAttribute(WebConst.USER);
            Set<MessageCriteria> criteria = (Set<MessageCriteria>)session.getAttribute(WebConst.CRITERIA);
            session.removeAttribute(WebConst.CRITERIA);
            session.removeAttribute("contacts");
            session.removeAttribute("accounts");
            List<FileUploadBean> attachments = (List<FileUploadBean>)session.getAttribute(WebConst.ATTACHMENTS);
            session.removeAttribute(WebConst.ATTACHMENTS);
            //msg
            Message message = new Message();
            message.setSubject(selectSend.getSubject());
            message.setCreationTime(new Date());
            message.setCreator(creator);
            message.setContent(selectSend.getContent()); //save content?
            int attSize = attachments != null ? attachments.size() : 0;
            message.setAttachments(attSize);
            message.setCriteria(criteria);
            for(MessageCriteria mc : criteria) {
                mc.setMessage(message);
            }
            int accountId = RequestUtils.getRequiredIntParameter(request, "account");
            Account account = (Account)getBasicDAO().loadEntity(Account.class, accountId);
            message.setAccount(account);
            boolean isHtml = RequestUtils.getStringParameter(request, "html") != null;
            message.setHtml(isHtml);
            getBasicDAO().saveOrUpdateEntity(message);
            getBasicDAO().evict(message); // remove from session - to be able to handle in another session
            //content
            MimeMessageHelper helper = new MimeMessageHelper(getMailClient().createMimeMessage(), true, encoding);
            helper.setFrom(account.getEmail(), account.getName());
            helper.setSubject(message.getSubject());
            helper.setText(selectSend.getContent(), isHtml);
            addAttachments(helper, attachments);
            saveAttachments(message, attachments);
            //do send
            int[] contactIds = selectSend.getContact();
            Sender sender = new Sender(contactIds, helper, message, selectSend);
            taskExecutor.execute(sender);
            int rzise = contactIds != null ? contactIds.length : 0;
            Map<String, Object> model = new HashMap<String, Object>();
            model.put("rsize", rzise);
//            model.put("successful", new BeanCollection(successful));
//            model.put("failed", new BeanCollection(failed));
            return new ModelAndView(getResultView(), model);
        }
    }

    private void saveAttachments(Message message, List<? extends ByteAdapter> attachments) {
        String state = null;
        try {
            if (attachments != null && !attachments.isEmpty()) {
                state = "saving";
                attachmentHandler.saveAttachments(String.valueOf(message.getId()), attachments);
            }
            state = "removing";
            attachmentHandler.removeAttachments();
        }
        catch (Exception e) {
            logger.warn("Exception while " + state + " attachments, ignored.", e);
        }
    }

    /* setters && getters */

    public String getResultView() {
        return resultView;
    }

    public void setResultView(String resultView) {
        this.resultView = resultView;
    }

    private class Sender implements Runnable {

        private int[] contactIds;
        private MimeMessageHelper helper;
        private Message message;
        private SelectSend selectSend;

        public Sender(int[] contactIds, MimeMessageHelper helper, Message message, SelectSend selectSend) {
            this.contactIds = contactIds;
            this.helper = helper;
            this.message = message;
            this.selectSend = selectSend;
        }

        public void run() {
            sendMessage();
        }

        protected void sendMessage() {
            Set<Contact> successful = new HashSet<Contact>();
            Set<Contact> failed = new HashSet<Contact>();
            int contactSize = contactIds != null ? contactIds.length : 0;
            logger.info("Sending message '" + message + "', current contact size: " + contactSize);
            for (int i = 0; i < contactSize; i++) {
                Contact contact = null;
                try {
                    contact = getBasicDAO().getContact(contactIds[i]);
                    helper.setTo(new InternetAddress(selectSend.getEmail(contactIds[i]), contact.getLabel()));
                    getMailClient().sendMail(helper.getMimeMessage());
                    successful.add(contact);
                } catch (Throwable t) {
                    if (contact != null) {
                        logger.warn("Could not send email for contact " + contact + ": " + t);
                        failed.add(contact);
                    } else {
                        logger.error("Exception while fetching contact, id: " + contactIds[i]);
                    }
                }
            }
            try {
                message.setRecepients(successful);
                message.setFailed(failed);
                logger.info("Successful: " + successful.size() + ", msg_id: " + message.getId());
                logger.info("Failed: " + failed.size() + ", msg_id: " + message.getId());
                getBasicDAO().saveOrUpdateEntity(message);
                logger.info("Finished with sending msg '" + message + "', msg saved.");
            }
            catch (Throwable t) {
                logger.error("Exception while saving message: " + t);
            } finally {
                sendReport(message);
            }
        }
    }
}
